package com.delu.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.delu.config.RequestConfig;
import com.delu.data.Result;
import com.delu.em.ResultEnum;
import com.delu.entity.*;
import com.delu.mapper.*;

import com.delu.utils.JwtUtil;
import com.delu.utils.SetId;
import io.jsonwebtoken.Claims;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@Api
@RestController
@RequestMapping("site")
public class SiteController {

    @Autowired
    private SiteCarMapper siteCarMapper;

    @Autowired
    private AreaMapper areaMapper;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private SiteMapper siteMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private ProvinceMapper provinceMapper;

    @Autowired
    private CityMapper cityMapper;

    @Autowired
    private LabelShowMapper labelShowMapper;

    @GetMapping("findProvince")
    @ApiOperation("查询所有省")
    public Result getProvince() {

        if (redisTemplate.hasKey("province")) {

            Integer ps = (Integer) redisTemplate.boundValueOps("provinceSize").get();

            List<Province> provinces = (List<Province>) redisTemplate.boundValueOps("province").get();

            return Result.response(ResultEnum.SUCCESS).setData(provinces);

        } else {

            List<Province> provinces = provinceMapper.selectAll();

            redisTemplate.boundValueOps("province").set(provinces);

            return Result.response(ResultEnum.SUCCESS).setData(provinces);
        }

    }

    @GetMapping("findCity")
    @ApiOperation("查询所有的市")
    public Result getCity() {

        if (redisTemplate.hasKey("city")) {

            List<Cities> provinces = (List<Cities>) redisTemplate.boundValueOps("city").get();

            return Result.response(ResultEnum.SUCCESS).setData(provinces);

        } else {

            List<Cities> provinces = cityMapper.selectAll();

            redisTemplate.boundValueOps("city").set(provinces);

            return Result.response(ResultEnum.SUCCESS).setData(provinces);
        }

    }

    @GetMapping
    @ApiOperation("调用百度地图接口查询当前定位所在的市")
    public Result getUserAddr(HttpServletRequest request) {

        String ak = "H4Qd47pl0Wwrf6BTqx69FfGZhGFzEG3P";

        String forObject = restTemplate.getForObject("http://api.map.baidu.com/location/ip?ak=" + ak, String.class);

        return Result.response(ResultEnum.SUCCESS).setData(forObject);
    }

    @GetMapping("findSite/{cityid}")
    @ApiOperation("根据市的id查询所有对应的区县,并保存到了redis当中")
    @ApiImplicitParam(value = "cityid", name = "市id", required = true, paramType = "path")
    public Result getArea(@PathVariable("cityid") String cityid) {

        if (redisTemplate.boundHashOps("city").hasKey(cityid)) {

            List<Area> areas = (List<Area>) redisTemplate.boundHashOps("city").get(cityid);

            return Result.response(ResultEnum.SUCCESS).setData(areas);

        }else {

            QueryWrapper<Area> query = Wrappers.query();

            query.eq("cityid", cityid);

            List<Area> areas = areaMapper.selectList(query);

            redisTemplate.boundHashOps("city").put(cityid, areas);

            return Result.response(ResultEnum.SUCCESS).setData(areas);
        }
    }

    @GetMapping("/{areaid}")
    @ApiOperation("根据区县id查询所属的站点信息")
    @ApiImplicitParam(value = "areaid", name = "区县id", required = true, paramType = "path")
    public Result getSite(@PathVariable("areaid") String areaid) {

        if (redisTemplate.boundHashOps("site").hasKey(areaid)) {

            List<Site> sites = (List<Site>) redisTemplate.boundHashOps("site").get(areaid);

            return Result.response(ResultEnum.SUCCESS).setData(sites);

        }else {

            QueryWrapper<Site> query = Wrappers.query();

            query.eq("area_id", Integer.parseInt(areaid));

            List<Site> sites = siteMapper.selectList(query);

            redisTemplate.boundHashOps("site").put(areaid, sites);

            return Result.response(ResultEnum.SUCCESS).setData(sites);
        }

    }


    @GetMapping("getSiteCar/{siteid}")
    @ApiOperation("根据站点id,查询出站点中的所有车辆信息")
    @ApiImplicitParam(value = "siteid", name = "站点id", required = true, paramType = "path")
    public Result getSiteCar(@PathVariable("siteid") String siteid, HttpServletRequest request) throws IOException {

        /*String authToken = request.getHeader("authToken");

        Claims claims = jwtUtil.parseJwt(authToken);

        String id = claims.getId();*/

        /*if (redisTemplate.boundHashOps("car").hasKey(siteid)) {

            List<SiteCar> sc = (List<SiteCar>) redisTemplate.boundHashOps("car").get(siteid);

            return Result.response(ResultEnum.SUCCESS).setData(sc);

        }else {

            QueryWrapper<SiteCar> query = Wrappers.query();

            Set<ZSetOperations.TypedTuple<String>> set2 = redisTemplate.opsForZSet().reverseRangeByScoreWithScores(id + "show", 0, -1);

            List<Integer> ilist = new ArrayList<>();

            for (ZSetOperations.TypedTuple<String> ss : set2) {

                QueryWrapper<Label> query1 = Wrappers.query();

                String value1 = ss.getValue();

                query1.eq("label_name", value1);

                Label label = labelShowMapper.selectOne(query1);

                ilist.add(label.getLabelId());
            }

            List<SiteCar> siteCars = new ArrayList<>();

            for (int i = 0; i < ilist.size(); i++) {

                query.eq("label_id", ilist.get(i)).and(w -> w.eq("site_id", siteid));

                siteCarMapper.selectList(query).stream().forEach(s -> siteCars.add(s));
            }

            query.notIn("lable_id", ilist).and(w -> w.eq("site_id", siteid));

            siteCarMapper.selectList(query).stream().forEach(s -> siteCars.add(s));

            redisTemplate.boundHashOps("car").put(siteid, siteCars);

            return Result.response(ResultEnum.SUCCESS).setData(siteCars);

        }*/

        String authToken = request.getHeader("authToken");

        Claims claims = jwtUtil.parseJwt(authToken);

        String id = claims.getId();

        String userKey = id + "user";

        SearchRequest searchRequest = new SearchRequest("site_car");

        SearchSourceBuilder builder = new SearchSourceBuilder();

        if (redisTemplate.hasKey(userKey)) {

            Set<ZSetOperations.TypedTuple> set = redisTemplate.boundZSetOps(userKey).reverseRangeWithScores(0, -1);

            List<String> collect = set.stream().map(u -> (String) u.getValue()).collect(Collectors.toList());

            BoolQueryBuilder label = QueryBuilders.boolQuery();

            label.must(QueryBuilders.termsQuery("label", collect)).must(QueryBuilders.matchPhraseQuery("site_id", siteid));

            builder.query(label);

            searchRequest.source(builder);

            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHit[] hits = response.getHits().getHits();

            List<SiteCar> cars = new ArrayList<>();

            for (SearchHit hit : hits) {

                String sourceAsString = hit.getSourceAsString();

                SiteCar siteCar = JSONObject.parseObject(sourceAsString, SiteCar.class);

                cars.add(siteCar);
            }

            return Result.response(ResultEnum.SUCCESS).setData(cars);

        }else  {

            MatchPhraseQueryBuilder matchPhraseQuery = QueryBuilders.matchPhraseQuery("site_id", siteid);

            builder.query(matchPhraseQuery);

            searchRequest.source(builder);

            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchHit[] hits = response.getHits().getHits();

            List<SiteCar> cars = new ArrayList<>();

            for (SearchHit hit : hits) {

                String sourceAsString = hit.getSourceAsString();

                SiteCar siteCar = JSONObject.parseObject(sourceAsString, SiteCar.class);

                cars.add(siteCar);
            }

            return Result.response(ResultEnum.SUCCESS).setData(cars);

        }

    }

    @Scheduled(cron = "0 30 * * * ?")
    public void setLabel() {

        String id = SetId.getId();

        String numKey = id + "num";

        String timeKey = id + "time";

        String userKey = id + "user";

        AtomicInteger i = new AtomicInteger(3);

        Set<ZSetOperations.TypedTuple> numSet = null;

        Set<ZSetOperations.TypedTuple> timeSet = null;

        Set<ZSetOperations.TypedTuple> userSet = null;

        if (redisTemplate.boundZSetOps(numKey).range(0, -1).size() >= 3 && redisTemplate.boundZSetOps(timeKey).range(0, -1).size() >= 3) {

            numSet = redisTemplate.boundZSetOps(numKey).reverseRangeWithScores(0, 2);

            timeSet = redisTemplate.boundZSetOps(timeKey).reverseRangeWithScores(0, 2);
        } else {

            numSet = redisTemplate.boundZSetOps(numKey).reverseRangeWithScores(0, -1);

            timeSet = redisTemplate.boundZSetOps(timeKey).reverseRangeWithScores(0, -1);
        }

        numSet.stream().forEach(n -> {

            redisTemplate.boundZSetOps(userKey).add(n.getValue(), i.getAndDecrement());
        });

        if (redisTemplate.boundZSetOps(userKey).range(0, -1).size() >= 3) {

            userSet = redisTemplate.boundZSetOps(numKey).reverseRangeWithScores(0, 2);

        } else {

            userSet = redisTemplate.boundZSetOps(numKey).reverseRangeWithScores(0, -1);

        }

        Set<ZSetOperations.TypedTuple> finalTimeSet = timeSet;

        userSet.stream().forEach(u -> {

            finalTimeSet.stream().forEach(t -> {

                if (u.getValue().equals(t.getValue())) {

                    redisTemplate.boundZSetOps(userKey).incrementScore(u.getValue(), 3);
                } else {

                    redisTemplate.boundZSetOps(userKey).add(t.getValue(), 3);
                }
            });
        });

        redisTemplate.expire(userKey, 30, TimeUnit.MINUTES);
    }
}
